//== SAGITTARIUS ============================================================================

//== FILEDOC =========================================================================

/** @file llvm_interf.h
  * @brief llvm_interf
  */

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGUTTARUIS__LLVM_TYPES__H_
#define __SAGUTTARUIS__LLVM_TYPES__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/types/types.h"
#include "sagittarius/builtins/builtins.h"
#include "sagittarius/builtins/builtinfun.h"
#include "sagittarius/backend/llvm/llvm_interf.h"

#ifdef __cplusplus

//LLVM
#include "llvm/Support/DataTypes.h"
#include "llvm/DerivedTypes.h"
#include "llvm/IRBuilder.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Analysis/Verifier.h"


namespace Sagittarius {
    
    class LLVM_Primitives {
        public:
            
            void initPrimitives() {
                i8_tp = llvm::IntegerType::get(llvm::getGlobalContext(), 8);
                i16_tp = llvm::IntegerType::get(llvm::getGlobalContext(), 16);
                i32_tp = llvm::IntegerType::get(llvm::getGlobalContext(), 32);
                i64_tp = llvm::IntegerType::get(llvm::getGlobalContext(), 64);
                
                void_tp = llvm::Type::getVoidTy(llvm::getGlobalContext());
                pvoid = llvm::PointerType::get(i8_tp,0);
                
                #if SAGITTARIUS_ARCH == SAGITTARIUS_ARCH_64
                    lp_tp = i64_tp;
                #elif SAGITTARIUS_ARCH == SAGITTARIUS_ARCH_64
                    lp_tp = i32_tp;
                #endif
                
                // symcode type
                symcode_tp = i64_tp;
            }
            
            /// ints
            llvm::Type* i8_tp;
            llvm::Type* i16_tp;
            llvm::Type* i32_tp;
            llvm::Type* i64_tp;
            
            /// void ptr
            llvm::Type* void_tp;
            llvm::Type* pvoid;
            
            /// pointer type as uint
            llvm::Type* lp_tp;
            
            /// Type that symcode uses (uint64)
            llvm::Type* symcode_tp;
            
    };
    
    class LLVM_Types : public LLVM_Primitives {
        public:
            LLVM_Types();
            
            void initSGTypes1();
            
            std::vector<llvm::Type*> make_obj_header();
            
            llvm::StructType* getFunVal_IRDef();
            
            llvm::StructType* getListVal_IRDef();
            
            
            
            llvm::StructType* val;
            uint64 val_header_off;
            llvm::PointerType* pval;
            llvm::PointerType* ppval;
            
            // sg_fun_val
            llvm::StructType* funval;
            llvm::Type* pfunval;
    };
    
    LLVM_Types* getLLVMTypes();
    
    class LLVM_Util {
        public:
            LLVM_Util();
            
            LLVM_Types* types() { return llvmt; }
            const LLVM_Types* types() const { return llvmt; }
            
            void initFunctions();
            
            void mapFunctions(llvm::ExecutionEngine* e);
            
            void storeFP(sg_fun_val* f, void* addr);
            
            void checkFunValTp(llvm::Value* fv, llvm::IRBuilder<>* builder);
            
        protected:
            LLVM_Types* llvmt;
            
            llvm::Module* mod;
            
            // store fp
            llvm::Function* f_store_fp;
            llvm::FunctionType* f_store_fp_tp;
            
            // store fp
            llvm::Function* f_check_fvaltype;
            llvm::FunctionType* f_check_fvaltype_tp;
    };
    
    LLVM_Util* getLLVMUtil();
    
    
}
#endif

#endif
